<template>
  <el-container>
    <el-header>
      <el-row :gutter="20">
        <el-col :span="12">
          <el-select v-model="selectedPort" placeholder="选择端口" style="width: 100%;">
            <el-option
              v-for="port in ports"
              :key="port"
              :label="port"
              :value="port">
            </el-option>
          </el-select>
        </el-col>
        <el-col :span="6">
          <el-button type="primary" @click="refreshPorts" style="width: 100%;">刷新端口</el-button>
        </el-col>
        <el-col :span="6">
          <el-button
            :type="isConnected ? 'danger' : 'primary'"
            :style="isConnected ? 'background-color: red; color: white; width: 100%;' : 'width: 100%;'"
            @click="isConnected ? disconnectFromPort() : connectToPort()"
          >
            {{ isConnected ? '断开' : '连接' }}
          </el-button>
        </el-col>
      </el-row>
      <el-row :gutter="20" style="margin-top: 10px;">
        <el-col :span="8">
          <el-button type="primary" style="width: 100%;" @click="openFileDialog">选择SN数据</el-button>
        </el-col>
        <el-col :span="8" style="display: flex; align-items: center;">
          <span>{{ filename }}</span>
        </el-col>
      </el-row>
    </el-header>
    <el-main style="height: 100%;" v-loading="isLoading">
      <el-form :model="form" label-width="120px" class="form-container" style="margin-top: 20px;">
        <el-form-item label="Manufacturer ID">
          <el-input v-model="form.manufacturerId" placeholder="Enter Manufacturer ID" maxlength="1"></el-input>
        </el-form-item>
        <el-form-item label="Project Code">
          <el-input v-model="form.projectCode" placeholder="Enter Project Code" maxlength="2"></el-input>
        </el-form-item>
        <el-form-item label="Version">
          <el-input v-model="form.version" placeholder="Enter Version" maxlength="4"></el-input>
        </el-form-item>
        <el-form-item label="Protocol Version">
          <el-input v-model="form.protocolVersion" placeholder="Enter Protocol Version" maxlength="2"></el-input>
        </el-form-item>
        <el-form-item label="Product ID">
          <el-input v-model="form.productId" placeholder="Enter Product ID"></el-input>
        </el-form-item>
        <el-form-item label="Key">
          <el-input v-model="form.key" placeholder="Enter Key"></el-input>
        </el-form-item>
        <el-form-item label="Serial Number">
          <el-input v-model="form.sn" placeholder="Serial Number"></el-input>
          <div>上次已写序号: {{ lastSn }}</div>
        </el-form-item>
        <el-form-item>
          <el-button type="primary" @click="sendData">发送数据</el-button>
        </el-form-item>
      </el-form>
    </el-main>
    <el-footer>
      <div class="align-center" style="display: flex; width: 100%; justify-content: space-between;">
        <div>
          <span>已写号设备数: {{ processedDevices }} 总设备数: {{ totalDevices }}</span>
        </div>
        <div>
          <span>当前软件版本: {{ version }}</span>
        </div>
      </div>
      <el-button type="info" @click="switchDebug">打开信息</el-button>
      <el-input
        v-if="openDebug"
        v-model="logs"
        type="textarea"
        :rows="5"
        readonly
        placeholder="Operation logs will appear here..."
        class="log-textarea"
      />
    </el-footer>
  </el-container>
</template>

<script setup lang="ts">
import { ref, onMounted } from 'vue';
import { ElMessage, ElMessageBox } from 'element-plus';
import { invoke } from '@tauri-apps/api/core';
import { listen } from '@tauri-apps/api/event';
import { open } from '@tauri-apps/plugin-dialog';
import { el, fa } from 'element-plus/es/locales.mjs';
enum DeviceState {
  STATE_WAIT_ACK,
  STATE_WAIT_DATA
};

enum RecvState {
  RECV_STATE_LENGTH,
  RECV_STATE_DATA
};

const logs = ref('')
const version = ref('');
let deviceKey: string | null = null;
const ports = ref<string[]>([]);
const lastSn = ref('');
const openDebug = ref(false);
const selectedPort = ref<string>('');
const form = ref({
  productId: '1010',
  key: '0d349114807c7d329c9fea7a4d92950c',
  sn: '',
  manufacturerId: 'S',
  projectCode: '01',
  version: '2501',
  protocolVersion: '11'
});
const isConnected = ref(false);
const ackReceived = ref(false);
const filename = ref('选择SN数据表');
const processedDevices = ref(0);
let currentIndex = 0;
const totalDevices = ref(0);
let totalSNInfo = [];
let validSnList = [];

const buffers: number[] = [];
let state = DeviceState.STATE_WAIT_ACK;
let recvState = RecvState.RECV_STATE_LENGTH;
let dataLength = 0;
let waitAckTimer = 0;
let unlisten: any;

const ackWaitingList = new Map<number, any>();
const isLoading = ref(false);

const refreshPorts = async () => {
  try {
    ports.value = await invoke<string[]>('enumerate_ports');
    ElMessage.success('Ports refreshed successfully');
  } catch (error) {
    ElMessage.error('Failed to refresh ports');
  }
};

onMounted(refreshPorts);

const arrayToHexString = (arr: number[]): string => {
  return Array.from(arr, byte => byte.toString(16).padStart(2, '0')).join(' ');
};

const appendLog = (message: string) => {
  logs.value += `${new Date().toLocaleTimeString()} - ${message}\n`;
};

const connectToPort = async () => {
  try {
    isLoading.value = true;
    deviceKey = await invoke<string>('connect_to_port', { port: selectedPort.value, baudrate: 115200 });
    ElMessage.success('Connected successfully');

    unlisten = await listen('serial-data', (event: any) => {
      console.log('Event received', event);
      buffers.push(...event.payload);

      if (state === DeviceState.STATE_WAIT_ACK) {
        if (buffers.length >= 4) {
          if (buffers[0] === 0xEA && buffers[3] === 0xAE) {
            state = DeviceState.STATE_WAIT_DATA;
            version.value = `${buffers[1]}.${buffers[2]}`;
            buffers.splice(0, 4);
            console.log("Received ack-ind ");
            if (waitAckTimer) {
              clearTimeout(waitAckTimer);
            }
            isConnected.value = true;
            isLoading.value = false;
          }
        }
      } else if (state === DeviceState.STATE_WAIT_DATA) {
        console.log("Received data", buffers);
        appendLog("Received data: " + arrayToHexString(buffers));
        
        while(buffers.length > 0) {
          if (recvState === RecvState.RECV_STATE_LENGTH) {
            if (buffers.length >= 2) {
              dataLength = ((buffers[1] & 0xFF) << 8) | (buffers[0] & 0xFF);
              console.log("Received data length", dataLength);
              recvState = RecvState.RECV_STATE_DATA;
              buffers.splice(0, 2);
            }else {
              break;
            }
          }else {
            console.log("Received data", buffers.length, dataLength);
            if (buffers.length >= dataLength) {
              const cmd = buffers[0];
              if (ackWaitingList.has(cmd)) {
                const callback = ackWaitingList.get(cmd);
                callback(buffers.slice(1, dataLength));
                ackWaitingList.delete(cmd);
                buffers.splice(0, dataLength);
              }
              recvState = RecvState.RECV_STATE_LENGTH;
              dataLength = 0;
            }else {
              break;
            }
          }
        }
      }
    });

    // Send the 3 bytes
    console.log("Sending 3 bytes");
    await invoke('write_to_port', { key: deviceKey, data: [0xEA, 0xFF, 0xAE] });

    // Set a timer to check for 'ack-ind' event
    waitAckTimer = setTimeout(() => {
      if (!ackReceived.value) {
        ElMessage.error('设备无响应');
        isConnected.value = false;
        isLoading.value = false;

        console.log("No response from device");
        invoke('close_port', { key: deviceKey });
        ElMessage.error('No response from device');

        

        //unregister event serial-data
        if (unlisten) {
          unlisten();
          unlisten = null;
        }
      }
    }, 5000); // 5 seconds timeout

  } catch (error) {
    ElMessage.error('连接失败' + error);
    isLoading.value = false;
    isConnected.value = false;
  }
};

const disconnectFromPort = async () => {
  try {
    await invoke('close_port', { key: deviceKey });
    ElMessage.success('设备已断开');
    isConnected.value = false;
    recvState = RecvState.RECV_STATE_LENGTH;
    state = DeviceState.STATE_WAIT_ACK;

    if (unlisten) {
      unlisten();
      unlisten = null;
    }
  } catch (error) {
    ElMessage.error('断开失败');
  }
};

const sendSnResult = async (rspData: number[]) => {
  isLoading.value = false;
  let resultCode = ((rspData[1] & 0xFF) << 8) | (rspData[0] & 0xFF);

  if (waitAckTimer) {
    clearTimeout(waitAckTimer);
    waitAckTimer = 0;
  }

  if (resultCode === 0x00) {
    const index = totalSNInfo.findIndex((item: any) => item.sn === form.value.sn);
    console.log('totalSNInfo Index:', index);
    
    if (index != -1) {
      totalSNInfo[index].valid = 'N';
      //filename: String, row_index: u32, value: i32
      try {
        const ret = await invoke('write_excel_cell', {
          filename: filename.value, 
          rowIndex: index + 1, 
          value: 0
        });
        console.log('Write excel cell:', ret);
      }catch (error) {
        console.log('error:', error);
        ElMessage.error('写excel失败' + error)
      }
    }

    const validIndex = validSnList.findIndex((item: any) => item.sn === form.value.sn);
    console.log('Valid index:', validIndex);

    if (validIndex >= 0) {
      validSnList[validIndex].valid = 'N';
      currentIndex += 1;
      processedDevices.value += 1;
    }   
    lastSn.value = form.value.sn;
    form.value.sn = validSnList[currentIndex].sn;
    ElMessage.success({
      message: 'Data processed successfully',
    });
  }else {
    lastSn.value = '';
    ElMessage.error({
      message: 'Failed to process data',
    });
  }
}

const sendData = async () => {
  
  // construct data by product id and key string, and sn
  // each item has length indicator before the actual data
  const productId = form.value.productId;
  const key = form.value.key;
  const sn = form.value.sn;
  const manufacturerId = form.value.manufacturerId;
  const projectCode = form.value.projectCode.padEnd(2, '0');
  const version = form.value.version.padEnd(4, '0');
  const protocolVersion = form.value.protocolVersion.padEnd(2, '0');

  // if there is no valid of validlist is none N, then notification data is all used
  console.log(validSnList.filter((item: any) => item.valid != 'N'));

  if (totalSNInfo.length === 0) {
    ElMessageBox({
      title: 'SN数据表未读取',
      message: '请先读取SN数据表',
      type: 'error',
      showClose: false
    });
    return;
  }
  

  if (validSnList.filter((item: any) => item.valid != 'N').length === 0) {
    ElMessageBox({
      title: 'SN数据已用完',
      message: 'SN数据已用完, 请选择新的SN数据，或清除源数据表的第二列',
      type: 'error',
      showClose: false
    });
    return;
  }
  isLoading.value = true;
  
  const data = [
    0xE0,
    // using 2 bytes to represent the length of the string
    ...manufacturerId.split('').map((char: string) => char.charCodeAt(0)),
    ...projectCode.split('').map((char: string) => char.charCodeAt(0)),
    ...version.split('').map((char: string) => char.charCodeAt(0)),
    ...protocolVersion.split('').map((char: string) => char.charCodeAt(0)),
    productId.length & 0xFF, (productId.length >> 8) & 0xFF,
    ...productId.split('').map((char: string) => char.charCodeAt(0)),
    key.length & 0xFF, (key.length >> 8) & 0xFF,
    ...key.split('').map((char: string) => char.charCodeAt(0)),
    sn.length & 0xFF, (sn.length >> 8) & 0xFF,
    ...sn.split('').map((char: string) => char.charCodeAt(0))
  ];

  // insert the length of the data at the beginning
  data.unshift(data.length & 0xFF, (data.length >> 8) & 0xFF);
 
  try {
    await invoke('write_to_port', { data });
    ackWaitingList.set(0xE0, sendSnResult);

    waitAckTimer = setTimeout(() => {
      isLoading.value = false;
      ElMessage.error('发送命令无响应');
    }, 5000); // 5 seconds timeout
  } catch (error) {
    console.log('error:', error);
    ElMessage.error('发送失败' + error);
    isLoading.value = false;
  }
};

const openFileDialog = async () => {
  let filepath = await open();

  filename.value = filepath;
  console.log('Filepath:', filepath);
  currentIndex = 0;
  processedDevices.value = 0;
  validSnList = [];
  totalSNInfo = [];

  try {
    totalSNInfo = await invoke('read_excel_column', { filename: filepath });
    console.log('Read excel column:', totalSNInfo);

    totalSNInfo.filter((item: any) => item.valid != 'N').forEach((item: any) => {
      validSnList.push(item);
    });

    totalDevices.value = totalSNInfo.length;

    processedDevices.value = totalSNInfo.length - validSnList.length;

    form.value.sn = validSnList[currentIndex].sn;

    console.log('Valid SN list:', validSnList);
    
    ElMessage.success('读取成功');
  }catch (error) {
    console.log('error:', error);
    ElMessage.error('读取失败');
  }
};

const switchDebug = async () => {
  openDebug.value = !openDebug.value;
  await invoke('switch_debug', {open: openDebug.value});
};

</script>

<style scoped>
.el-container {
  background-color: #F0F0F0;
  width: 600px;
  margin: 0 auto;
  border: 1px solid #ccc;
  border-radius: 8px;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
.el-header, .el-footer {
  background-color: #f5f5f5;
  padding: 10px;
}

.align-right {
  text-align: right;
}

.align-center {
  text-align: center;
}

.form-container {
  padding: 20px;
}

.log-textarea {
  margin-top: 10px;
  width: 100%;
}

.el-footer {
  height: auto !important;
  padding: 20px;
}

</style>
